﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using SlamCms.Common;
using SlamCms.Services;

namespace SlamCms.Web
{
	public enum BootstrapperPriority
	{
		Low = 0,
		Normal = 5,
		High = 10
	}

	public abstract class Bootstrapper
	{
		protected Bootstrapper() : this(BootstrapperPriority.Normal)
		{	
		}

		protected Bootstrapper(BootstrapperPriority priority)
		{
			Priority = priority;
			Name = this.GetType().Name;
		}

		public BootstrapperPriority Priority { get; protected set; }
		public string Name { get; protected set; }

		public virtual void PreApplicationStart(PreApplicationStartContext context)
		{}

		public virtual void PostApplicationStart(PostApplicationStartContext context)
		{}

		public static void Run(HttpApplication httpApplication)
		{
			var bootstrappers = GetBootstrappers();

			var builder = new ContainerBuilder();

			var preApplicationStartContext = new PreApplicationStartContext()
			{
				Application = httpApplication,
				ContainerBuilder = builder
			};

			bootstrappers.ForEach(b => b.PreApplicationStart(preApplicationStartContext));
			

		    IContainer container;

            try
            {
                container = builder.Build();
            }
            catch (ReflectionTypeLoadException ex)
            {
                var sb = new StringBuilder();

                sb.AppendLine("LOADED TYPES:");
                foreach (var type in ex.Types)
                {
                    if (type == null)
                        continue;

                    sb.AppendLine("- " + type.ToString());
                }

                sb.AppendLine("LOADER EXCEPTIONS");
                foreach (var exSub in ex.LoaderExceptions)
                {
                    sb.AppendLine(exSub.Message);
                    if (exSub is FileNotFoundException)
                    {
                        var exFileNotFound = exSub as FileNotFoundException;
                        if(!string.IsNullOrEmpty(exFileNotFound.FusionLog))
                        {
                            sb.AppendLine("Fusion Log:");
                            sb.AppendLine(exFileNotFound.FusionLog);
                        }
                    }
                    sb.AppendLine();
                }

                throw new Exception(sb.ToString(), ex);
            }

			DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
			SlamCmsConfiguration.Instance.DependencyResolver = new AutofacSlamCmsDependencyResolver(container);

			// simulate a request lifetime scope
			using (var bootstrapperScope = AutofacDependencyResolver.Current.RequestLifetimeScope)
			{
				var postApplicationStartContext = new PostApplicationStartContext()
				{
					Application = httpApplication,
					Container = container,
					BootstrapperLifetimeScope = bootstrapperScope
				};

				bootstrappers.ForEach(b => b.PostApplicationStart(postApplicationStartContext));
			}
		}

		private static IList<Bootstrapper> GetBootstrappers()
		{
			var list = new List<Bootstrapper>();

			foreach (var bootstrapperType in GetBootstrapperTypes())
			{
				var instance = GetBootstrapperInstance(bootstrapperType);
				list.Add(instance);	
			}

			return list.OrderByDescending(x => x.Priority).ThenBy(x => x.Name).ToList();
		}

		private static Bootstrapper GetBootstrapperInstance(Type bootstrapperType)
		{
			var instance = Activator.CreateInstance(bootstrapperType) as Bootstrapper;
			
			if (instance == null)
				throw new Exception("Cannot crate instance of Bootstrapper: {0}".F(bootstrapperType.FullName));

			return instance;
		}

		private static IEnumerable<Type> GetBootstrapperTypes()
		{
			return TypeHelpers.GetFilteredTypesFromAssemblies(t => t != typeof(Bootstrapper) && typeof(Bootstrapper).IsAssignableFrom(t));
		}
	}
}